#include <iostream>
#include <queue>
#include <vector>
#include <unistd.h>
#include <pthread.h>

//<--- 全局变量 --->
const int k_Shelf = 5;              //货架缓冲区最大容量
const int k_NumBaker = 2;           //面包师数量
const int k_NumCustomer = 4;        //消费者数量
const int k_BreadPerBaker = 10;     //每个面包师做的面包数量
const int k_BreadPerCustomer = 5;   //每个顾客消费的面包数量

//<--- 共享志愿 --->
std::queue<int> g_Shelf;                                      //共享货架队列
pthread_mutex_t g_Mutex = PTHREAD_MUTEX_INITIALIZER;          //临界志愿锁
pthread_cond_t g_CondNotFull = PTHREAD_COND_INITIALIZER;      //货架是否满了条件变量
pthread_cond_t g_CondNotEmpty = PTHREAD_COND_INITIALIZER;     //货架是否为空条件变量

//面包师线程方法
void* ThreadBakerFunc(void* args){
    //获取面包师ID
    long ID = reinterpret_cast<long>(args);
    
    //制作10个面包
    for(int i = 1; i <= k_BreadPerBaker; i++){
        //加锁
        pthread_mutex_lock(&g_Mutex);

        while(g_Shelf.size() == k_Shelf){//循环判断避免伪唤醒
            std::cout << "面包师：" << ID << "，发现面包货架已满，开始等待~" << std::endl;
            //货架已满，进行等待
            pthread_cond_wait(&g_CondNotFull, &g_Mutex);//等待+解锁
        }

        //可以制作面包
        int BreadID = static_cast<int>(ID) * 100 + i;
        //加入货架
        g_Shelf.push(BreadID);
        std::cout << "面包师：" << ID << "，考好了面包[" << BreadID << "]。货架上还有 "
        << g_Shelf.size() << " 个面包" << std::endl;

        //唤醒沉睡的消费者，货架现在不空了
        pthread_cond_signal(&g_CondNotEmpty);

        //解锁退出临界区
        pthread_mutex_unlock(&g_Mutex);

        //模拟烤面包耗时，提高并发度
        usleep(1000 * 300);
    }
    return nullptr;
}

//消费者线程方法
void* ThreadCustomerFunc(void* args){
    //获取消费者ID
    long ID = reinterpret_cast<long>(args);

    //购买面包
    for(int i = 1; i <= k_BreadPerCustomer; i++){
        //加锁
        pthread_mutex_lock(&g_Mutex);

        while(g_Shelf.size() == 0){//循环判断，避免伪唤醒
            std::cout << "消费者：" << ID << "，发现货架是空的，排队等待~" << std::endl;
            //货架为空，排队
            pthread_cond_wait(&g_CondNotEmpty, &g_Mutex);
        }

        //消费者可以买面包
        int BreadID = g_Shelf.front();
        g_Shelf.pop();

        std::cout << "消费者：" << ID << "，买到面包[" << BreadID << "]。货架上还有 "
        << g_Shelf.size() << " 个面包" << std::endl;

        //购买了面包，货架有空位了，通知面包师
        pthread_cond_signal(&g_CondNotFull);

        //解锁
        pthread_mutex_unlock(&g_Mutex);

        //模拟购买后操作
        usleep(1000 * 1000);
    }
    return nullptr;
}


int main(){
    //<--- 初始化变量 --->
    std::vector<pthread_t> Threads(k_NumBaker + k_NumCustomer); //总线程数
    
    std::cout << "-------------------面包店开门了-------------------" << std::endl;

    //创建面包师线程，i使用long避免传参形成数据丢失
    for(long i = 1; i <= k_NumBaker; i++){
        pthread_create(&Threads[i - 1], nullptr, ThreadBakerFunc, (void*)i);
    }
    
    //创建消费者线程
    for(long i = 1; i <= k_NumCustomer; i++){
        pthread_create(&Threads[k_NumBaker + i - 1], nullptr, ThreadCustomerFunc, (void*)i);
    }

    //回收线程
    for(long i = 1; i <= k_NumBaker + k_NumCustomer; i++){
        pthread_join(Threads[i - 1], nullptr);
    }

    //销毁锁、条件变量资源
    pthread_mutex_destroy(&g_Mutex);
    pthread_cond_destroy(&g_CondNotEmpty);
    pthread_cond_destroy(&g_CondNotFull);

    std::cout << "-------------------面包店关门了-------------------" << std::endl;

    return 0;
}